// <copyright file="DoubleLiteral.cs" company="Alan Dean">
// The MIT License

// Copyright (c) 2008 Alan Dean

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// </copyright>
// <author>Alan Dean</author>
// <email>alan.dean@thoughtpad.net</email>

namespace Semantic.Rdf
{
    using System;
    using System.Globalization;
    using System.Runtime.Serialization;
    using System.Security.Permissions;
    using System.Xml;

    /// <summary>Represents an RDF double literal.</summary>
    [Serializable]
    public struct DoubleLiteral : IComparable, ISerializable, ITypedLiteral
    {
        #region fields

        /// <summary>
        /// Stores the <see cref="P:Semantic.Rdf.DoubleLiteral.DataType"/> data.
        /// </summary>
        private static readonly IUriReference _dataType = new UriReference("http://www.w3.org/2001/XMLSchema#double");

        /// <summary>
        /// Stores the <see cref="P:Semantic.Rdf.DateTimeLiteral.Epsilon"/> data.
        /// </summary>
        private static readonly DoubleLiteral _epsilon = new DoubleLiteral(Double.Epsilon);

        /// <summary>
        /// Stores the <see cref="P:Semantic.Rdf.DateTimeLiteral.MaxValue"/> data.
        /// </summary>
        private static readonly DoubleLiteral _maxValue = new DoubleLiteral(Double.MaxValue);

        /// <summary>
        /// Stores the <see cref="P:Semantic.Rdf.DateTimeLiteral.MinValue"/> data.
        /// </summary>
        private static readonly DoubleLiteral _minValue = new DoubleLiteral(Double.MinValue);

        /// <summary>
        /// Stores the <see cref="P:Semantic.Rdf.DateTimeLiteral.NaN"/> data.
        /// </summary>
        private static readonly DoubleLiteral _nan = new DoubleLiteral(Double.NaN);

        /// <summary>
        /// Stores the <see cref="P:Semantic.Rdf.DateTimeLiteral.NegativeInfinity"/> data.
        /// </summary>
        private static readonly DoubleLiteral _negativeInfinity = new DoubleLiteral(Double.NegativeInfinity);

        /// <summary>
        /// Stores the <see cref="P:Semantic.Rdf.DateTimeLiteral.PositiveInfinity"/> data.
        /// </summary>
        private static readonly DoubleLiteral _positiveInfinity = new DoubleLiteral(Double.PositiveInfinity);

        /// <summary>
        /// Stores the <see cref="T:Semantic.Rdf.DoubleLiteral"/> data.
        /// </summary>
        private double _value;

        #endregion

        #region public DoubleLiteral(double)
        /// <overloads>
        /// Overloaded.
        /// Initializes a new instance of the <see cref="T:Semantic.Rdf.DoubleLiteral"/> class.
        /// </overloads>
        ///
        /// <summary>
        /// Initializes a new instance of the <see cref="T:Semantic.Rdf.DoubleLiteral"/> class
        /// with the specified <paramref name="value"/>.
        /// </summary>
        ///
        /// <param name="value">The <see langword="double"/> value.</param>
        public DoubleLiteral(double value)
        {
            this._value = value;
        }
        #endregion

        #region public DoubleLiteral(string)
        /// <summary>
        /// Initializes a new instance of the <see cref="T:Semantic.Rdf.DoubleLiteral"/> class
        /// with the specified <paramref name="value"/>.
        /// </summary>
        ///
        /// <param name="value">The <see langword="string"/> value.</param>
        /// 
        /// <exception cref="T:System.ArgumentNullException">
        /// <paramref name="value"/> is a <see langword="null"/> reference.
        /// </exception>
        /// <exception cref="T:System.FormatException">
        /// <paramref name="value"/> is not in a correct format.
        /// </exception>
        public DoubleLiteral(string value)
        {
            if (null == value)
            {
                throw new ArgumentNullException("value");
            }

            this._value = XmlConvert.ToDouble(value);
        }
        #endregion

        #region private DoubleLiteral(SerializationInfo, StreamingContext)
        /// <summary>
        /// Initializes a new instance of the <see cref="T:Semantic.Rdf.DoubleLiteral"/> class
        /// from the specified instances of the <see cref="T:System.Runtime.Serialization.SerializationInfo"/>
        /// and <see cref="T:System.Runtime.Serialization.StreamingContext"/> classes.
        /// </summary>
        /// 
        /// <param name="info">
        /// An instance of the  <see cref="T:System.Runtime.Serialization.SerializationInfo"/> class
        /// containing the information required to serialize the
        /// new <see cref="T:Semantic.Rdf.DoubleLiteral"/> instance. 
        /// </param>
        /// <param name="context">
        /// An instance of the <see cref="T:System.Runtime.Serialization.StreamingContext"/> class
        /// containing the source of the serialized stream associated with the
        /// new <see cref="T:Semantic.Rdf.DoubleLiteral"/> instance.
        /// </param>
        /// 
        /// <exception cref="T:System.ArgumentNullException">
        /// <paramref name="info"/> is a <see langword="null"/> reference.
        /// </exception>
        private DoubleLiteral(SerializationInfo info, StreamingContext context)
        {
            if (null == info)
            {
                throw new ArgumentNullException("info");
            }

            this._value = info.GetDouble("_value");
        }
        #endregion

        #region public static DoubleLiteral Epsilon
        /// <summary>
        /// Gets the smallest possible value of <see cref="T:Semantic.Rdf.DoubleLiteral"/> that is greater than zero.
        /// This value is constant.
        /// </summary>
        public static DoubleLiteral Epsilon
        {
            get
            {
                return _epsilon;
            }
        }
        #endregion

        #region public static DoubleLiteral MaxValue
        /// <summary>
        /// Gets the largest possible value of <see cref="T:Semantic.Rdf.DoubleLiteral"/>.
        /// This value is constant.
        /// </summary>
        public static DoubleLiteral MaxValue
        {
            get
            {
                return _maxValue;
            }
        }
        #endregion

        #region public static DoubleLiteral MinValue
        /// <summary>
        /// Gets the smallest possible value of <see cref="T:Semantic.Rdf.DoubleLiteral"/>.
        /// This value is constant.
        /// </summary>
        public static DoubleLiteral MinValue
        {
            get
            {
                return _minValue;
            }
        }
        #endregion

        #region public static DoubleLiteral NaN
        /// <summary>
        /// Gets the smallest possible value of <see cref="T:Semantic.Rdf.DoubleLiteral"/>.
        /// This value is constant.
        /// </summary>
        public static DoubleLiteral NaN
        {
            get
            {
                return _nan;
            }
        }
        #endregion

        #region public static DoubleLiteral NegativeInfinity
        /// <summary>
        /// Gets a <see cref="T:Semantic.Rdf.DoubleLiteral"/> with the value of negative infinity.
        /// This value is constant.
        /// </summary>
        public static DoubleLiteral NegativeInfinity
        {
            get
            {
                return _negativeInfinity;
            }
        }
        #endregion

        #region public static DoubleLiteral PositiveInfinity
        /// <summary>
        /// Gets a <see cref="T:Semantic.Rdf.DoubleLiteral"/> with the value of positive infinity.
        /// This value is constant.
        /// </summary>
        public static DoubleLiteral PositiveInfinity
        {
            get
            {
                return _positiveInfinity;
            }
        }
        #endregion

        #region public IUriReference DataType
        /// <summary>
        /// Gets the XML Schema double datatype URI.
        /// </summary>
        /// 
        /// <value>
        /// A <see cref="T:Semantic.Rdf.IUriReference">URI reference</see>.
        /// </value>
        /// 
        /// <remarks>
        /// <para>Datatypes are used by RDF in the representation of values such as integers, floating point numbers and dates.</para>
        /// <para>
        /// A datatype consists of a lexical space, a value space and a lexical-to-value mapping,
        /// see <see href="http://www.w3.org/TR/rdf-concepts/#section-Datatypes">Datatypes (Normative)</see>.
        /// </para>
        /// </remarks>
        /// 
        /// <seealso href="http://www.w3.org/TR/rdf-concepts/#section-Datatypes-intro">Datatypes</seealso>
        /// <seealso href="http://www.w3.org/TR/rdf-concepts/#section-Literal-Value">The Value Corresponding to a Typed Literal</seealso>
        public IUriReference DataType
        {
            get
            {
                return _dataType;
            }
        }
        #endregion

        #region public string LexicalForm
        /// <summary>
        /// Gets the <see href="http://www.w3.org/TR/rdf-concepts/#dfn-lexical-form">lexical form</see>.
        /// </summary>
        /// 
        /// <value>
        /// A <see href="http://www.w3.org/TR/rdf-concepts/#ref-unicode">Unicode</see>
        /// <see langword="string"/>.
        /// </value>
        /// 
        /// <remarks>
        /// All literals have a lexical form being a <see href="http://www.w3.org/TR/rdf-concepts/#ref-unicode">Unicode</see> string,
        /// which SHOULD be in <see href="http://www.w3.org/TR/rdf-concepts/#ref-nfc">Normal Form C [NFC]</see>.
        /// </remarks>
        public string LexicalForm
        {
            get
            {
                return XmlConvert.ToString(this._value);
            }
        }
        #endregion

        #region public static implicit operator double(DoubleLiteral)
        /// <summary>
        /// Implicitly converts an instance of the
        /// <see cref="T:Semantic.Rdf.DoubleLiteral"/> type
        /// to a <see langword="double"/>.
        /// </summary>
        /// 
        /// <param name="value">The instance to implicitly convert.</param>
        /// 
        /// <returns>A <see langword="double"/> value.</returns>
        public static implicit operator double(DoubleLiteral value)
        {
            return value.ToDouble();
        }
        #endregion

        #region public static implicit operator DoubleLiteral(double)
        /// <summary>
        /// Implicitly converts a <see langword="double"/> value
        /// to an instance of the
        /// <see cref="T:Semantic.Rdf.DoubleLiteral"/> type.
        /// </summary>
        /// 
        /// <param name="value">The value to implicitly convert.</param>
        /// 
        /// <returns>
        /// An instance of the <see cref="T:Semantic.Rdf.DoubleLiteral"/> type.
        /// </returns>
        public static implicit operator DoubleLiteral(double value)
        {
            return new DoubleLiteral(value);
        }
        #endregion

        #region public static bool operator ==(DoubleLiteral, DoubleLiteral)
        /// <summary>Implements the equality operator for this type.</summary>
        /// 
        /// <param name="operand1">The first operand.</param>
        /// <param name="operand2">The second operand.</param>
        /// 
        /// <returns>
        /// Returns <see langword="true"/> if the values of the operands are equal;
        /// otherwise, <see langword="false"/>.
        /// </returns>
        /// 
        /// <remarks>The equality operator is strongly-typed.</remarks>
        public static bool operator ==(DoubleLiteral operand1, DoubleLiteral operand2)
        {
            return (0 == DoubleLiteral.Compare(operand1, operand2));
        }
        #endregion

        #region public static bool operator !=(DoubleLiteral, DoubleLiteral)
        /// <summary>Implements the inequality operator for this type.</summary>
        /// 
        /// <param name="operand1">The first operand.</param>
        /// <param name="operand2">The second operand.</param>
        /// 
        /// <returns>
        /// Returns <see langword="true"/> if the values of its operands are not equal;
        /// otherwise, <see langword="false"/>.
        /// </returns>
        /// 
        /// <remarks>The inequality operator is strongly-typed.</remarks>
        public static bool operator !=(DoubleLiteral operand1, DoubleLiteral operand2)
        {
            return (0 != DoubleLiteral.Compare(operand1, operand2));
        }
        #endregion

        #region public static bool operator <(DoubleLiteral, DoubleLiteral)
        /// <summary>Implements the less than operator for this type.</summary>
        /// 
        /// <param name="operand1">The first operand.</param>
        /// <param name="operand2">The second operand.</param>
        /// 
        /// <returns>
        /// Returns <see langword="true"/> if the first operand is less than the second operand;
        /// otherwise, <see langword="false"/>.
        /// </returns>
        /// 
        /// <remarks>The operator is strongly-typed.</remarks>
        public static bool operator <(DoubleLiteral operand1, DoubleLiteral operand2)
        {
            return (DoubleLiteral.Compare(operand1, operand2) < 0);
        }
        #endregion

        #region public static bool operator >(DoubleLiteral, DoubleLiteral)
        /// <summary>Implements the greater than operator for this type.</summary>
        /// 
        /// <param name="operand1">The first operand.</param>
        /// <param name="operand2">The second operand.</param>
        /// 
        /// <returns>
        /// Returns <see langword="true"/> if the first operands is greater than the second operand;
        /// otherwise, <see langword="false"/>.
        /// </returns>
        /// 
        /// <remarks>The operator is strongly-typed.</remarks>
        public static bool operator >(DoubleLiteral operand1, DoubleLiteral operand2)
        {
            return (DoubleLiteral.Compare(operand1, operand2) > 0);
        }
        #endregion

        #region public static int Compare(DoubleLiteral, DoubleLiteral)
        /// <summary>Compares two instances of this type.</summary>
        /// 
        /// <param name="comparand1">The first comparand.</param>
        /// <param name="comparand2">The second comparand.</param>
        /// 
        /// <returns>
        /// A 32-bit signed integer indicating the lexical relationship between the two comparands.
        /// <list type="table">
        /// <listheader>
        ///   <term>Value Type</term>
        ///   <description>Condition</description>
        /// </listheader>
        /// <item>
        ///   <term>Less than zero</term>
        ///   <description><paramref name="comparand1"/> is less than <paramref name="comparand2"/>.</description>
        /// </item>
        /// <item>
        ///   <term>Zero</term>
        ///   <description><paramref name="comparand1"/> equals <paramref name="comparand2"/>.</description>
        /// </item>
        /// <item>
        ///   <term>Greater than zero</term>
        ///   <description><paramref name="comparand1"/> is greater than <paramref name="comparand2"/>.</description>
        /// </item>
        /// </list>
        /// </returns>
        public static int Compare(DoubleLiteral comparand1, DoubleLiteral comparand2)
        {
            return object.ReferenceEquals(comparand1, comparand2)
                ? 0
                : string.Compare(comparand1.ToString(), comparand2.ToString(), StringComparison.OrdinalIgnoreCase);
        }
        #endregion

        #region public override bool Equals(object)
        /// <summary>
        /// Determines if the specified <paramref name="obj"/> is equal to this instance..
        /// </summary>
        /// 
        /// <param name="obj">The <see langword="object"/> to compare with this instance.</param>
        /// 
        /// <returns>
        /// Returns <see langword="true"/> if the specified <paramref name="obj"/> is equal to this instance;
        /// otherwise <see langword="false"/>.
        /// </returns>
        public override bool Equals(object obj)
        {
            bool result = false;

            if (!object.ReferenceEquals(null, obj))
            {
                if (object.ReferenceEquals(this, obj))
                {
                    result = true;
                }
                else
                {
                    if (obj is DoubleLiteral)
                    {
                        result = (0 == DoubleLiteral.Compare(this, (DoubleLiteral)obj));
                    }
                }
            }

            return result;
        }
        #endregion

        #region public override int GetHashCode()
        /// <summary>Returns the hash code for this instance.</summary>
        /// <returns>A 32-bit signed integer hash code.</returns>
        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }
        #endregion

        #region public override string ToString()
        /// <summary>
        /// Returns a <see langword="string"/> representation of this instance.
        /// </summary>
        /// <returns>A <see langword="string"/> representation.</returns>
        public override string ToString()
        {
            return string.Format(CultureInfo.InvariantCulture, "\"{0}\"^^<{1}>", this.LexicalForm, this.DataType.LexicalForm);
        }
        #endregion

        #region public int CompareTo(object)
        /// <summary>
        /// Compares the current instance with another <see langword="object"/> of the same type.
        /// </summary>
        /// 
        /// <param name="obj">An <see langword="object"/> to compare with this instance.</param>
        /// 
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared.
        /// The return value has these meanings:
        /// <list type="table">
        /// <listheader>
        ///   <term>Value Type</term>
        ///   <description>Meaning</description>
        /// </listheader>
        /// <item>
        ///   <term>Less than zero</term>
        ///   <description>This instance is less than <paramref name="obj"/>.</description>
        /// </item>
        /// <item>
        ///   <term>Zero</term>
        ///   <description>This instance is equal to <paramref name="obj"/>.</description>
        /// </item>
        /// <item>
        ///   <term>Greater than zero</term>
        ///   <description>This instance is greater than <paramref name="obj"/>.</description>
        /// </item>
        /// </list>
        /// </returns>
        /// 
        /// <exception cref="T:System.ArgumentException">
        /// <paramref name="obj"/> is not an instance of the
        /// <see cref="T:Semantic.Rdf.DoubleLiteral"/> type.
        /// </exception>
        public int CompareTo(object obj)
        {
            int comparison = 1;

            if (!object.ReferenceEquals(null, obj))
            {
                if (obj is DoubleLiteral)
                {
                    comparison = DoubleLiteral.Compare(this, (DoubleLiteral)obj);
                }
                else
                {
                    throw new ArgumentException(
                        string.Format(CultureInfo.InvariantCulture, Properties.Resources.ObjectMustBeOfTypeException_Message_Format, "DoubleLiteral"),
                        "obj");
                }
            }

            return comparison;
        }
        #endregion

        #region void ISerializable.GetObjectData(SerializationInfo, StreamingContext)
        /// <summary>Returns the data needed to serialize the current instance.</summary>
        /// 
        /// <param name="info">
        /// A <see cref="T:System.Runtime.Serialization.SerializationInfo"/> object containing the information
        /// required to serialize the <see cref="T:Semantic.Rdf.DoubleLiteral"/>.
        /// </param>
        /// <param name="context">
        /// A <see cref="T:System.Runtime.Serialization.StreamingContext"/> object containing the source and destination
        /// of the serialized stream associated with the <see cref="T:Semantic.Rdf.DoubleLiteral"/>.
        /// </param>
        /// 
        /// <exception cref="T:System.ArgumentNullException">
        /// <paramref name="info"/> is a <see langword="null"/> reference.
        /// </exception>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (null == info)
            {
                throw new ArgumentNullException("info");
            }

            info.AddValue("_value", this._value);
        }
        #endregion

        #region public double ToDouble()
        /// <summary>Returns the <see langword="double"/> value.</summary>
        /// 
        /// <returns>A <see langword="double"/> representation of this literal.</returns>
        public double ToDouble()
        {
            return this._value;
        }
        #endregion
    }
}